<template>
  <el-dialog :visible="show" destroy-on-close custom-class="clip-dialog" @dragstart.stop.prevent="">
    <canvas id="img-canvas" ref="imgCanvas"></canvas>
    <div
      class="clipRect"
      :style="clipRectStyle"
      @mousedown.stop="e => handleMouseDown(e, 'rect')"
      v-show="ctx && show"
    >
      <span
        v-for="key in ['tl', 'tc', 'tr', 'cl', 'cr', 'bl', 'bc', 'br']"
        :key="key"
        :class="['dot', key]"
        @mousedown.stop="e => handleMouseDown(e, key)"
        @dragstart.stop.prevent=""
      ></span>

      <div class="line hor hor1"></div>
      <div class="line hor hor2"></div>
      <div class="line ver ver1"></div>
      <div class="line ver ver2"></div>
    </div>

    <el-row class="bar" :style="{ bottom: screenHeight + 'px' }">
      <el-col :span="6">
        <div @click="handleCancel">取消</div>
      </el-col>
      <el-col :span="6">
        <div @click="handleRotate(-90)"><i class="el-icon-refresh-left"></i></div>
      </el-col>
      <el-col :span="6">
        <div @click="handleRotate(90)"><i class="el-icon-refresh-right"></i></div>
      </el-col>
      <el-col :span="6">
        <div @click="handleClipImg">上传</div>
      </el-col>
    </el-row>
  </el-dialog>
</template>

<script>
import Vue from 'vue'
import { Dialog, Row, Col, Icon } from 'element-ui'

Vue.use(Dialog)
Vue.use(Row)
Vue.use(Col)
Vue.use(Icon)

export default {
  name: 'CimImageProcess',
  props: {
    // 高度差
    screenHeight: {
      type: Number,
      default: 90
    }
  },
  data () {
    return {
      // dialog显示状态
      show: false,
      // 图片数据
      img: null,
      imgName: '',
      // canvas
      ctx: null,
      canvasConfig: {
        width: 0,
        height: 0
      },
      boundary: {
        top: 0,
        left: 0
      },
      // 裁剪框
      clipRectConfig: {
        width: 0,
        height: 0,
        top: 0,
        left: 0
      },
      // 其他
      mouseInfo: {
        down: false,
        lastX: 0,
        lastY: 0
      },
      // 旋转角度
      rotateDeg: 0
    }
  },
  computed: {
    clipRectStyle () {
      let { width, height, top, left } = this.clipRectConfig

      return {
        width: width + 'px',
        height: height + 'px',
        top: top + 'px',
        left: left + 'px'
      }
    }
  },
  methods: {
    // ref 调用
    clip (imgFile, name = 'clip.jpg') {
      let imgData = this.getObjectUrl(imgFile)

      this.show = true
      this.imgName = name
      this.img = new Image()
      this.img.src = imgData
      this.img.onload = () => {
        this.initClip()
      }
    },
    // tools
    // 图片文件获取图片base64
    getObjectUrl (file) {
      let url = null

      if (window.createObjectURL !== undefined) {
        url = window.createObjectURL(file)
      } else if (window.URL !== undefined) {
        url = window.URL.createObjectURL(file)
      } else if (window.webkitURL !== undefined) {
        url = window.webkitURL.createObjectURL(file)
      }
      return url
    },
    dataUrlToBlob (dataurl) {
      let arr = dataurl.split(',')
      let mime = arr[0].match(/:(.*?);/)[1]
      let bstr = atob(arr[1])
      let n = bstr.length
      let u8arr = new Uint8Array(n)

      while (n--) {
        u8arr[n] = bstr.charCodeAt(n)
      }
      return new Blob([u8arr], { type: mime })
    },
    blobToFile (theBlob, fileName) {
      theBlob.lastModifiedDate = new Date()
      theBlob.name = fileName
      return theBlob
    },
    // 计算canvas尺寸
    computedCanvasSize (useOrigin = false) {
      let windowW = window.innerWidth - 50
      let windowH = window.innerHeight - 100 - this.screenHeight
      let { width: imgWidth, height: imgHeight } = this.img
      let canvasW = imgWidth
      let canvasH = imgHeight

      if (this.rotateDeg % 180 !== 0) {
        [imgWidth, imgHeight] = [imgHeight, imgWidth]
        if (useOrigin) [canvasW, canvasH] = [canvasH, canvasW]
      }

      if (!useOrigin) {
        if (imgWidth >= windowW) {
          canvasW = windowW
          canvasH = imgHeight / imgWidth * windowW
        } else {
          canvasW = imgWidth
          canvasH = imgHeight
        }

        if (canvasH > windowH) {
          canvasH = windowH
          canvasW = imgWidth / imgHeight * windowH
        }
      }

      this.canvasConfig = {
        width: canvasW,
        height: canvasH
      }
      this.boundary = {
        top: 25,
        left: (windowW - canvasW) / 2 + 25
      }
    },
    // 设置画布和裁剪框
    setCanvas () {
      let canvas = this.$refs.imgCanvas
      let { width, height } = this.canvasConfig
      let { top, left } = this.boundary

      canvas.width = width
      canvas.height = height
      this.clipRectConfig = {
        width,
        height,
        top,
        left
      }
      this.ctx = canvas.getContext('2d')
    },
    // 绘制图片
    drawImage () {
      if (!this.ctx) return

      let { width, height } = this.canvasConfig

      if (this.rotateDeg % 180 !== 0) {
        [width, height] = [height, width]
      }

      this.ctx.drawImage(this.img, 0, 0, width, height)
    },
    // 旋转画布
    rotateCanvas (width, height) {
      if (!width && !height) {
        width = this.canvasConfig.width
        height = this.canvasConfig.height
      }
      // let { width, height } = this.canvasConfig
      let transWidth = width
      let transHeight = height

      if (this.rotateDeg % 180 !== 0) {
        [transWidth, transHeight] = [height, width]
      }

      this.ctx.translate(width / 2, height / 2)
      this.ctx.rotate(this.rotateDeg * Math.PI / 180)
      this.ctx.translate(-transWidth / 2, -transHeight / 2)
    },
    // handle
    // 鼠标按下，dot拉伸
    handleMouseDown (e, key) {
      this.mouseInfo = Object.assign({}, this.mouseInfo, {
        down: key,
        lastX: e.clientX,
        lastY: e.clientY
      })
    },
    handleMouseMove (e) {
      if (!this.mouseInfo.down) return

      let { down, lastX, lastY } = this.mouseInfo
      let { clientX: x, clientY: y } = e
      let distanceX = x - lastX
      let distanceY = y - lastY
      let baseX = 0
      let baseY = 0
      let baseTop = 0
      let baseLeft = 0
      let { width, height, top, left } = this.clipRectConfig
      let { width: imgWidth, height: imgHeight } = this.canvasConfig
      let { top: boundTop, left: boundLeft } = this.boundary

      if (down === 'tl') {
        baseX = -1
        baseY = -1
        baseTop = 1
        baseLeft = 1
      } else if (down === 'tc') {
        baseY = -1
        baseTop = 1
      } else if (down === 'tr') {
        baseX = 1
        baseY = -1
        baseTop = 1
      } else if (down === 'cl') {
        baseX = -1
        baseLeft = 1
      } else if (down === 'cr') {
        baseX = 1
      } else if (down === 'bl') {
        baseX = -1
        baseY = 1
        baseLeft = 1
      } else if (down === 'bc') {
        baseY = 1
      } else if (down === 'br') {
        baseX = 1
        baseY = 1
      } else if (down === 'rect') {
        baseTop = 1
        baseLeft = 1
      }

      let newWidth = width + distanceX * baseX
      let newHeight = height + distanceY * baseY
      let newTop = top + distanceY * baseTop
      let newLeft = left + distanceX * baseLeft

      // 边界
      if (down !== 'rect') {
        if (newWidth < 0) {
          newLeft = left
          down = false
        }
        if (newHeight < 0) {
          newTop = top
          down = false
        }
        if (newLeft < boundLeft) {
          newLeft = boundLeft
          newWidth = width
        }
        if (newTop < boundTop) {
          newTop = boundTop
          newHeight = height
        }
        if (newWidth + newLeft - boundLeft > imgWidth) {
          newWidth = imgWidth + boundLeft - newLeft
        }
        if (newHeight + newTop - boundTop > imgHeight) {
          newHeight = imgHeight + boundTop - newTop
        }
      } else {
        if (newLeft < boundLeft) {
          newLeft = boundLeft
        }
        if (newTop < boundTop) {
          newTop = boundTop
        }
        if (newWidth + newLeft - boundLeft > imgWidth) {
          newLeft = imgWidth + boundLeft - newWidth
        }
        if (newHeight + newTop - boundTop > imgHeight) {
          newTop = imgHeight + boundTop - newHeight
        }
      }
      this.clipRectConfig = Object.assign({}, this.clipRectConfig, {
        width: newWidth,
        height: newHeight,
        top: newTop,
        left: newLeft
      })
      this.mouseInfo = Object.assign({}, this.mouseInfo, {
        lastX: x,
        lastY: y,
        down
      })
    },
    handleMouseUp () {
      this.mouseInfo.down = false
    },
    initClip () {
      this.computedCanvasSize()
      this.setCanvas()
      this.ctx.save()
      this.drawImage()
    },
    // 取消
    handleCancel () {
      this.img = null
      this.show = false
      this.ctx.restore()
      this.ctx = null
      this.rotateDeg = 0
      this.$emit('cancel')
    },
    // 旋转
    handleRotate (deg) {
      if (!this.ctx) return

      this.ctx.restore()
      this.rotateDeg += deg
      this.computedCanvasSize()
      this.setCanvas()
      this.rotateCanvas()
      this.drawImage()
    },
    // 裁剪
    handleClipImg () {
      if (!this.ctx) return

      this.ctx.restore()
      let { width: canvasW, height: canvasH } = this.canvasConfig
      let { width: rectW, height: rectH, top, left } = this.clipRectConfig
      let { width: imgWidth, height: imgHeight } = this.img
      let { top: boundTop, left: boundLeft } = this.boundary
      let scaleX = 1
      let scaleY = 1
      let canvas = this.$refs.imgCanvas
      let newCanvasW = 0
      let newCanvasH = 0
      let offsetX = 0
      let offsetY = 0

      if (this.rotateDeg % 180 === 0) {
        scaleX = imgWidth / canvasW
        scaleY = imgHeight / canvasH
      } else {
        scaleX = imgHeight / canvasW
        scaleY = imgWidth / canvasH
      }
      newCanvasW = scaleX * rectW
      newCanvasH = scaleY * rectH

      canvas.width = newCanvasW
      canvas.height = newCanvasH
      if ((this.rotateDeg % 360 + 360) / 90 % 4 === 0) {
        // 0
        offsetX = -(left - boundLeft) * scaleY
        offsetY = -(top - boundTop) * scaleX
      } else if ((this.rotateDeg % 360 + 360) / 90 % 4 === 1) {
        // 90
        offsetX = -(top - boundTop) * scaleX
        offsetY = -(canvasW - left + boundLeft - rectW) * scaleY
      } else if ((this.rotateDeg % 360 + 360) / 90 % 4 === 2) {
        // 180
        offsetX = -(canvasW - left + boundLeft - rectW) * scaleY
        offsetY = -(canvasH - top + boundTop - rectH) * scaleX
      } else if ((this.rotateDeg % 360 + 360) / 90 % 4 === 3) {
        // 270
        offsetX = -(canvasH - top + boundTop - rectH) * scaleX
        offsetY = -(left - boundLeft) * scaleY
      }
      // offsetX = -(left - bountLeft) * scaleY
      // offsetY = -(top - boundTop) * scaleX
      this.rotateCanvas(newCanvasW, newCanvasH)
      this.ctx.drawImage(this.img, offsetX, offsetY, imgWidth, imgHeight)
      this.$nextTick(() => {
        let base64Data = this.$refs.imgCanvas.toDataURL('image/jpg', 1)
        let blobData = this.dataUrlToBlob(base64Data)
        let file = this.blobToFile(blobData)

        this.show = false
        this.$emit('clip', [file, base64Data])
        this.ctx.restore()
        this.ctx = null
        this.rotateDeg = 0
      })
    }
  },
  mounted () {
    // 绑定事件
    window.addEventListener('mousemove', this.handleMouseMove)
    window.addEventListener('mouseup', this.handleMouseUp)
  },
  destroyed () {
    window.removeEventListener('mousemove', this.handleMouseMove)
    window.removeEventListener('mouseup', this.handleMouseUp)
  }
}
</script>

<style lang="less" scope>
  .clip-dialog{
    .clipRect{
      position: absolute;
      box-sizing: border-box;
      border: 1px solid #aaa;
      cursor: move;

      .dot{
        width: 30px;
        height: 30px;
        position: absolute;

        &:hover{
          &::before, &::after{
            background-color: orange;
          }
        }

        &::before{
          content: '';
          position: absolute;
          width: 30px;
          height: 7px;
          background-color: #fff;
        }

        &.tl, &.tc, &.tr{
          &::before{
            top: 0;
            left: 0;
          }
        }

        &.bl, &.bc, &.br{
          &::before{
            bottom: 0;
            left: 0;
          }
        }

        &.cl, &.cr{
          &::before{
            display: none;
          }
        }

        &::after{
          content: '';
          position: absolute;
          height: 30px;
          width: 7px;
          background-color: #fff;
        }

        &.tl, &.cl, &.bl{
          &::after{
            top: 0px;
            left: 0px;
          }
        }

        &.tr, &.cr, &.br{
          &::after{
            top: 0px;
            right: 0px;
          }
        }

        &.tc, &.bc{
          &::after{
            display: none;
          }
        }

        &.tl{
          top: -3px;
          left: -4px;
          cursor: nw-resize;
        }

        &.tc{
          top: -3px;
          right: calc(50% - 15px);
          cursor: n-resize;
        }

        &.tr{
          top: -3px;
          right: -4px;
          cursor: ne-resize;
        }

        &.cl{
          top: calc(50% - 3px);
          left: -4px;
          cursor: e-resize;
        }

        &.cr{
          top: calc(50% - 3px);
          right: -4px;
          cursor: e-resize;
        }

        &.bl{
          bottom: -3px;
          left: -4px;
          cursor: ne-resize;
        }

        &.bc{
          bottom: -3px;
          left: calc(50% - 15px);
          cursor: n-resize;
        }

        &.br{
          bottom: -3px;
          right: -4px;
          cursor: nw-resize;
        }
      }

      .line{
        position: absolute;
        background-color: #aaa;

        &.hor{
          height: 1px;
          width: 100%;
          left: 0;

          &.hor1{
            top: 33.3%;
          }

          &.hor2{
            top: 66.6%;
          }
        }

        &.ver{
          width: 1px;
          height: 100%;
          top: 0;

          &.ver1{
            left: 33.3%;
          }

          &.ver2{
            left: 66.6%;
          }
        }
      }
    }
  }
</style>

<style lang='less'>
  .clip-dialog{
    background: none;
    margin: 0 !important;
    box-shadow: none;
    width: 100%;
    height: 100vh;
    overflow: hidden;

    .el-dialog__header{
      display: none;
    }

    .el-dialog__body{
      padding: 0;
      text-align: center;
      height: 100vh;

      #img-canvas{
        margin-top: 25px;
      }

      .bar{
        position: absolute;
        bottom: 0;
        width: 100%;
        height: 50px;
        background-color: #fff;
        font-size: 16px;
        line-height: 50px;
        text-align: center;
        cursor: pointer;
      }
    }
  }
</style>
